#include "BankSystem.hh"
#include <iostream>
#include <iomanip>
#include <random>
#include <fstream>
#include <ctime>
#include <sstream>
#include <limits>
#include <regex>
#ifdef _WIN32
#include <cstdlib>
#define CLEAR_SCREEN "cls"
#else
#define CLEAR_SCREEN "clear"
#endif
#include "tool.hh"


BankSystem::BankSystem() {
    customers.reserve(100); // Pre-allocate space for customers
    usedAccountIds.reserve(200); // Pre-allocate space for account IDs
}



std::string BankSystem::generateAccountId(const std::string& accountType) const {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> branchDist(1000, 9999);
    std::uniform_int_distribution<> accountDist(1000, 9999);

    std::stringstream ss;
    ss << "62" << (accountType == "Savings" ? "21" : "22") << "-"
       << std::setw(4) << std::setfill('0') << branchDist(gen) << "-"
       << std::setw(4) << std::setfill('0') << accountDist(gen) << "-"
       << std::setw(4) << std::setfill('0') << accountDist(gen);

    std::string accountId = ss.str();
    if (!isUniqueAccountId(accountId)) {
        return generateAccountId(accountType);
    }
    return accountId;
}

bool BankSystem::isUniqueAccountId(const std::string& accountId) const {
    return std::find(usedAccountIds.begin(), usedAccountIds.end(), accountId) == usedAccountIds.end();
}

bool BankSystem::validateAccountId(const std::string& accountId) const {
    std::regex pattern("^62(21|22)-[0-9]{4}-[0-9]{4}-[0-9]{4}$");
    return std::regex_match(accountId, pattern);
}

Customer* BankSystem::findCustomer(const std::string& name) const {
    for (const auto& customer : customers) {
        if (customer->getName() == name) {
            return customer.get();
        }
    }
    return nullptr;
}

BankAccount* BankSystem::findAccount(const std::string& accountId) const {
    for (const auto& customer : customers) {
        if (customer->getSavingsAccount()->getAccountId() == accountId) {
            return customer->getSavingsAccount();
        }
        if (customer->getCheckingAccount()->getAccountId() == accountId) {
            return customer->getCheckingAccount();
        }
    }
    return nullptr;
}

void BankSystem::registerCustomer(const std::string& name) {
    if (name.empty() || name.length() > 50) {
        std::cout << "Error: Customer name must be non-empty and less than 50 characters.\n";
        return;
    }
    if (findCustomer(name)) {
        std::cout << "Error: Customer already exists.\n";
        return;
    }

    std::string savingsId = generateAccountId("Savings");
    std::string checkingId = generateAccountId("Checking");

    customers.push_back(std::make_unique<Customer>(name, savingsId, checkingId));
    usedAccountIds.push_back(savingsId);
    usedAccountIds.push_back(checkingId);

    std::cout << "Customer " << name << " successfully registered!\n";
    std::cout << "Two bank accounts have been created:\n";
    std::cout << "- Savings Account: " << savingsId << "\n";
    std::cout << "- Checking Account: " << checkingId << "\n";
}

void BankSystem::displayAllAccounts() const {
    double totalBalance = 0.0;
    int totalAccounts = 0;

    std::cout << "===== All Bank Accounts =====\n\n";
    std::cout << "SAVINGS ACCOUNTS:\n";
    int index = 1;
    for (const auto& customer : customers) {
        auto* account = customer->getSavingsAccount();
        std::cout << std::left << std::setw(3) << index++ << ". ID: " << std::setw(20) << account->getAccountId()
                  << " | Owner: " << std::setw(20) << account->getOwnerName()
                  << " | Balance: " << std::fixed << std::setprecision(2) << account->getBalance() << "\n";
        totalBalance += account->getBalance();
        totalAccounts++;
    }

    std::cout << "\nCHECKING ACCOUNTS:\n";
    index = 1;
    for (const auto& customer : customers) {
        auto* account = customer->getCheckingAccount();
        std::cout << std::left << std::setw(3) << index++ << ". ID: " << std::setw(20) << account->getAccountId()
                  << " | Owner: " << std::setw(20) << account->getOwnerName()
                  << " | Balance: " << std::fixed << std::setprecision(2) << account->getBalance() << "\n";
        totalBalance += account->getBalance();
        totalAccounts++;
    }

    std::cout << "\nTotal Accounts: " << totalAccounts << "\n";
    std::cout << "Total Customers: " << customers.size() << "\n";
    std::cout << "Total Balance: " << std::fixed << std::setprecision(2) << totalBalance << "\n";
}

bool BankSystem::generateGlobalReport() const {
    std::time_t now = std::time(nullptr);
    std::stringstream ss;
    ss << std::put_time(std::localtime(&now), "transactions_%Y-%m-%d.txt");
    std::string filename = ss.str();

    std::ofstream file(filename);
    if (!file.is_open()) {
        std::cout << "Error: Could not create report file: " << filename << "\n";
        return false;
    }

    file << "Global Transaction Report\n\n";
    for (const auto& customer : customers) {
        file << "Customer: " << customer->getName() << "\n";
        file << "Savings Account: " << customer->getSavingsAccount()->getAccountId() << "\n";
        file << std::left << std::setw(10) << "ID"
             << std::setw(20) << "Date"
             << std::setw(15) << "Type"
             << std::setw(10) << "Amount"
             << "Note\n";
        file << std::string(65, '-') << "\n";

        for (const auto& t : customer->getSavingsAccount()->getTransactions()) {
            file << std::left << std::setw(10) << t.getTransactionId()
                 << std::setw(20) << t.getDate()
                 << std::setw(15) << t.getType()
                << std::fixed << std::setprecision(2) << std::setw(9) << t.getAmount()
                 << t.getNote() << "\n";
        }

        file << "\nChecking Account: " << customer->getCheckingAccount()->getAccountId() << "\n";
        file << std::left << std::setw(10) << "ID"
             << std::setw(20) << "Date"
             << std::setw(15) << "Type"
             << std::setw(10) << "Amount"
             << "Note\n";
        file << std::string(65, '-') << "\n";

        for (const auto& t : customer->getCheckingAccount()->getTransactions()) {
            file << std::left << std::setw(10) << t.getTransactionId()
                 << std::setw(20) << t.getDate()
                 << std::setw(15) << t.getType()
                 << "¥" << std::fixed << std::setprecision(2) << std::setw(9) << t.getAmount()
                 << t.getNote() << "\n";
        }
        file << "\n";
    }

    file.close();
    std::cout << "Report successfully generated as " << filename << "\n";
    return true;
}

